package redis

import (
	"context"
	"gitee.com/dennis-kk/service-box-go/util/errors"
	"github.com/go-redis/redis/v8"
)

type (
	//IClient redis 客户端接口包装，统一 client 和 cluster client 便于使用
	IClient interface {
		redis.Cmdable
		Init(...Option) error
		Start() error
		UnInit() error
		IsValid() bool
	}

	// client 单节点客户端
	client struct {
		*redis.Client
		options *Options
	}

	// clusterClient 集群模式客户端
	slaveClient struct {
		*redis.ClusterClient
		options *Options
	}
)

func NewClient(opts ...Option) IClient {

	option := &Options{}
	for _, opt := range opts {
		opt(option)
	}

	return &client{
		nil,
		option,
	}
}

func NewClusterClient(opts ...Option) IClient {
	option := &Options{}
	for _, opt := range opts {
		opt(option)
	}

	return &slaveClient{
		nil,
		option,
	}
}

//Init 根据配置建立redis客户端
func (c *client) Init(opts ...Option) error {
	// 应用设置
	for _, opt := range opts {
		opt(c.options)
	}
	// 初始化Redis
	rCfg := redis.Options{
		Addr:     c.options.AddrHosts[0],
		Username: c.options.UserName,
		Password: c.options.Password,
		DB:       c.options.DB,
		PoolSize: c.options.PoolSize,
	}
	c.Client = redis.NewClient(&rCfg)
	return nil
}

//Start 启动redis客户端
func (c *client) Start() error {
	if c.Client == nil {
		return errors.RedisNotInit
	}

	// 测试ping pong， 确认是否准备好
	if err := c.Client.Ping(context.Background()).Err(); err != nil {
		c.Client = nil
		return err
	}
	return nil
}

// UnInit 断开和redis的连接
func (c *client) UnInit() error {
	if c.Client == nil {
		return nil
	}

	defer func() {
		c.Client = nil
	}()

	return c.Client.Close()
}

//IsValid 返回是否成功初始化redis原始客户端
func (c *client) IsValid() bool {
	return c.Client != nil
}

//Init 根据配置建立redis cluster 客户端
func (c *slaveClient) Init(options ...Option) error {
	// 应用设置
	for _, opt := range options {
		opt(c.options)
	}

	// 初始化Redis
	rCfg := redis.ClusterOptions{
		Addrs:     c.options.AddrHosts,
		NewClient: c.newClient,
		Username:  c.options.UserName,
		Password:  c.options.Password,
		PoolSize:  c.options.PoolSize,
		ReadOnly:  c.options.ReadOnly,
	}

	c.ClusterClient = redis.NewClusterClient(&rCfg)
	return nil
}

func (c *slaveClient) Start() error {
	if c.ClusterClient == nil {
		return errors.RedisNotInit
	}

	// 测试ping pong， 确认是否准备好
	if err := c.ClusterClient.Ping(context.Background()).Err(); err != nil {
		c.ClusterClient = nil
		return err
	}
	return nil
}

func (c *slaveClient) UnInit() error {
	if c.ClusterClient == nil {
		return nil
	}

	defer func() {
		c.ClusterClient = nil
	}()

	return c.ClusterClient.Close()
}

func (c *slaveClient) IsValid() bool {
	return c.ClusterClient != nil
}

//TODO 实现不同节点不同配置
//newClient 自定义客户端创建需求，可以实现不同节点不同的设置
func (c *slaveClient) newClient(option *redis.Options) *redis.Client {
	option.DB = c.options.DB
	return redis.NewClient(option)
}
